package com.rest.core.dao;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.util.Assert;

import com.rest.core.utils.Pager;
import com.rest.core.utils.Pager.OrderType;


/**
 * HibernateDao
 * @ClassName: HibernateDao
 * @Description: Hibernate通用CRUD操作
 * @author xuzw
 * @date 2015年8月27日 下午10:29:37
 * @param <T>
 * @param <PK>
 */
public class HibernateDao<T, PK extends Serializable> extends BaseDao<T, PK> {

    /**
     * 用于Dao层子类的构造函数. 通过子类的泛型定义取得对象类型Class. eg. public class UserDao extends
     * BaseDaoImpl<User, Long>{ }
     */
    public HibernateDao() {
        super();
    }

    /**
     * 用于省略Dao层, Service层直接使用通用BaseDaoImpl的构造函数. 在构造函数中定义对象类型Class. eg.
     * BaseDaoImpl<User, Long> userDao = new BaseDaoImpl<User,
     * Long>(sessionFactory, User.class);
     */
    public HibernateDao(final SessionFactory sessionFactory,
            final Class<T> entityClass) {
        super(sessionFactory, entityClass);
    }

    // -- 分页查询函数 --//

    /**
     * 按HQL分页查询.
     * @param page  分页参数. 注意不支持其中的orderBy参数.
     * @param hql   hql语句.
     * @param values 数量可变的查询参数,按顺序绑定.
     * @return 分页查询结果, 附带结果列表及所有分页查询参数
     */
    @SuppressWarnings("rawtypes")
	public Pager findPage(final com.rest.core.utils.Pager pager, final String hql,
            final Object... values) {
        Assert.notNull(pager, "page不能为空");
        Query q = createQuery(hql, values);
        long totalCount = countHqlResult(hql, values);
        pager.setTotalCount(totalCount);
        setPageParameterToQuery(q, pager);
        List result = q.list();
        pager.setList(result);
        return pager;
    }

    /**
     * 按HQL分页查询.
     * @param page   分页参数. 注意不支持其中的orderBy参数.
     * @param hql    hql语句.
     * @param values 命名参数,按名称绑定.
     * @return 分页查询结果, 附带结果列表及所有分页查询参数
     */
    @SuppressWarnings("rawtypes")
    public Pager findPage(final Pager pager, final String hql, final Map<String, ?> values) {
        Assert.notNull(pager, "page不能为空");
        Query q = createQuery(hql, values);
        long totalCount = countHqlResult(hql, values);
        pager.setTotalCount(totalCount);
        setPageParameterToQuery(q, pager);
        List result = q.list();
        pager.setList(result);
        return pager;
    }

    /**
     * 设置分页参数到Query对象,辅助函数.
     */
    protected Query setPageParameterToQuery(final Query q, final Pager pager) {
        Assert.isTrue(pager.getPageSize() > 0, "Page Size must larger than zero");
        // hibernate的firstResult的序号从0开始
        q.setFirstResult(pager.getFirst() - 1);
        q.setMaxResults(pager.getPageSize());
        return q;
    }

    /**
     * 执行count查询获得本次Hql查询所能获得的对象总数.
     * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
    	* @param	hql         当前查询Hql
    	* @param	Object...   查询条件
    	* @return Integer    返回类型
     */
    protected Integer countHqlResult(final String hql, final Object... values) {
        String countHql = prepareCountHql(hql);
        try {
            Integer count = findUnique(countHql, values);
            return count;
        } catch (Exception e) {
            throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
        }
    }

    /**
     * 执行count查询获得本次Hql查询所能获得的对象总数.
     * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
    	* @param hql         当前查询Hql
     * @param Map<String, ?>   查询条件
    	* @return Integer    返回类型
     */
    protected Integer countHqlResult(final String hql, final Map<String, ?> values) {
        String countHql = prepareCountHql(hql);
        try {
            Integer count = findUnique(countHql, values);
            return count;
        } catch (Exception e) {
            throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
        }
    }

    /**
     * 拼接总数Hql(截取查询字段，替换成count(*))
    	* @Title: prepareCountHql
    	* @param	 orgHql    当前查询的Hql
    	* @return String    查询总数的Hql
     */
    private String prepareCountHql(String orgHql) {
        String fromHql = orgHql;
        // select子句与order by子句会影响count查询,进行简单的排除.
        fromHql = "from " + StringUtils.substringAfter(fromHql, "from");
        fromHql = StringUtils.substringBefore(fromHql, "order by");
        String countHql = "select count(*) " + fromHql;
        return countHql;
    }
    
    /**
     * 分页查询（QBC查询）
    	* @param	pager     分页条件
    	* @param	dc        查询条件
    	* @return Pager    返回类型
     */
    @SuppressWarnings("rawtypes")
    public Pager findByPage(Pager pager, DetachedCriteria dc) {
        Criteria criteria = dc.getExecutableCriteria(getSession());
        Integer pageSize = pager.getPageSize();
        String orderBy = pager.getOrderBy();
        OrderType orderType = pager.getOrderType();
        Integer totalCount = Integer.parseInt(criteria.setProjection(Projections.rowCount()).uniqueResult().toString());
        criteria.setProjection(null);// 设置为null,表示查询所有
        criteria.setFirstResult(pager.getFirst() - 1);
        criteria.setMaxResults(pageSize);
        if (StringUtils.isNotEmpty(orderBy) && orderType != null) {
            if (orderType == OrderType.desc)
                criteria.addOrder(Order.asc(orderBy));
            else
                criteria.addOrder(Order.desc(orderBy));
        }
        List list = criteria.list();
        pager.setTotalCount(totalCount);
        pager.setList(list);
        return pager;
    }
}
